﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using Microsoft.Win32;

// GIT https://git01.codeplex.com/pidrna

namespace PidProject
{
    public partial class Form1 : Form
    {
        string Revision = "$Revision: 1.8 $";
        string ApplicationDirectory = "";
        string ApplicationNameNoExt = "";
        string CaptureDataDirectory = "";

        // image capture location
        int CaptureTop = 0;
        int CaptureLeft = 0;
        int CaptureRight = 1600;
        int CaptureBottom = 900;
        enum CaptureModeEnum { None, Left, Top, Rght, Bottom}
        CaptureModeEnum CaptureMode = CaptureModeEnum.None;

        int DigitLocation = 1;

        // Graphs
        //GraphPainter Gtop = null;
        //GraphPainter Gbot = null;

        public Form1()
        {
            InitializeComponent();
        }

        private bool LoadLineContainsKey(string line, string key)
        {
            return line.StartsWith(key);
        }
        private string LoadString(string line, string key)
        {
            if(!line.StartsWith(key)) { return ""; }
            return line.Remove(0, key.Length).Trim();
        }
        private int LoadInt(string line, string key)
        {
            if (!line.StartsWith(key)) { return 0; }
            string str = line.Remove(0, key.Length).Trim();

            int result = 0;
            try
            {
                result = Convert.ToInt32(str);
            }
            catch (Exception) { }
            return result;
        }
        private void LoadSettings()
        {
            string fname = ApplicationDirectory + "\\" + ApplicationNameNoExt + ".dat";
            if(File.Exists(fname))
            {
                string[] lines = File.ReadAllLines(fname);
                foreach(string s in lines)
                {
                         if (LoadLineContainsKey(s, "this.Top")) { this.Top = LoadInt(s, "this.Top"); }
                    else if (LoadLineContainsKey(s, "this.Left")) { this.Left = LoadInt(s, "this.Left"); }
                    else if (LoadLineContainsKey(s, "this.Height")) { this.Height = LoadInt(s, "this.Height"); }
                    else if (LoadLineContainsKey(s, "this.Width")) { this.Width = LoadInt(s, "this.Width"); }
                    else if (LoadLineContainsKey(s, "this.WindowState")) { this.WindowState = (FormWindowState) LoadInt(s, "this.WindowState"); }

                    else if (LoadLineContainsKey(s, "CaptureTop")) { CaptureTop = LoadInt(s, "CaptureTop"); }
                    else if (LoadLineContainsKey(s, "CaptureLeft")) { CaptureLeft = LoadInt(s, "CaptureLeft"); }
                    else if (LoadLineContainsKey(s, "CaptureRight")) { CaptureRight = LoadInt(s, "CaptureRight"); }
                    else if (LoadLineContainsKey(s, "CaptureBottom")) { CaptureBottom = LoadInt(s, "CaptureBottom"); }
                }
            }

        }
        private void SaveSettings()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("this.Top                 " + (this.Top < 0 ? "0" : this.Top.ToString()));
            sb.AppendLine("this.Left                " + (this.Left < 0 ? "0" : this.Left.ToString()));
            sb.AppendLine("this.Height              " + (this.Height < 200 ? "200" : this.Height.ToString()));
            sb.AppendLine("this.Width               " + (this.Width < 200 ? "200" : this.Width.ToString()));
            sb.AppendLine("this.WindowState         " + ((int)this.WindowState).ToString());

            sb.AppendLine("CaptureTop               " + CaptureTop.ToString());
            sb.AppendLine("CaptureLeft              " + CaptureLeft.ToString());
            sb.AppendLine("CaptureRight             " + CaptureRight.ToString());
            sb.AppendLine("CaptureBottom            " + CaptureBottom.ToString());

            string fname = ApplicationDirectory + "\\" + ApplicationNameNoExt + ".dat";
            File.WriteAllText(fname, sb.ToString());
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Text = "PID Recorder and Analyser  v" + Revision.Replace("Revision:", "").Replace("$", "").Trim() +
                        SimpleOcr.Revision.Replace("Revision: 1", "").Replace("$", "").Trim() +
                        GraphPainter.Revision.Replace("Revision: 1", "").Replace("$", "").Trim();

            ApplicationDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
            ApplicationNameNoExt = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);

            CaptureDataDirectory = ApplicationDirectory + "\\CaptureData\\";
            if (!Directory.Exists(CaptureDataDirectory))
                Directory.CreateDirectory(CaptureDataDirectory);

            SetDefaultCaptureArea();
            pictureBox1.Visible = false;
            LoadSettings();

            // AAZ
            CaptureArea(true);

            //recognizeImageToolStripMenuItem_Click(null, EventArgs.Empty);
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            SaveSettings();
        }

        struct PidDataStruct
        {
            public double time;
            public double temp;
            public bool heating;

            public PidDataStruct(double ti, double te, bool h)
            {
                time = ti;
                temp = te;
                heating = h;
            }
        }

        List<PidDataStruct> PidData = new List<PidDataStruct>();
        DateTime StartCaptureTime = DateTime.Now;

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyValue == 112)  // F1 - next bitmap
            {
                txtDiag.Clear();

                CurrentBmpIndex++;
                if (CurrentBmpIndex >= BmpNames.Count)
                    CurrentBmpIndex = 0;

                Bitmap bmp = new Bitmap(BmpNames[CurrentBmpIndex]);

                pictureBox1.Visible = true;
                pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
                pictureBox1.Image = bmp;

                bool isHeaterOn = false;
                double Temp = 0;
                string Time = Path.GetFileNameWithoutExtension(BmpNames[CurrentBmpIndex]);

                BitmapOCR(bmp, ref isHeaterOn, ref Temp, null);
                txtDiag.AppendText(Time + "   " + (isHeaterOn ? " H " : " - ") + " " + Temp.ToString() + "\r\n");
            }
            if (e.KeyValue == 116)  // F5 - start recorder
            {
                PidData.Clear();
                StartCaptureTime = DateTime.Now;
                timer1.Enabled = true;
            }
            if (e.KeyValue == 117)  // F6 - stop recorder
            {
                timer1.Enabled = false;

                txtDiag.Clear();
                foreach (PidDataStruct p in PidData)
                    txtDiag.AppendText(p.time.ToString("0.00") + "\t " + p.temp.ToString() + "\t " + (p.heating ? "H" : "-") + "\r\n");

                txtTime.Text = "";
                txtTemp.Text = "";
                txtHeater.Text = "";
            }
        }

        private void splitContainer1_Panel1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.Clear(Color.White);
        }
        private void CaptureTestToolStripMenuItem_Click(object sender, EventArgs e)
        {
            timer1.Enabled = true;
        }

        private void SetDefaultCaptureArea()
        {
            Screen myScreen = Screen.FromControl(this);

            CaptureTop = 0;
            CaptureLeft = 0;
            CaptureBottom = myScreen.Bounds.Height;
            CaptureRight = myScreen.Bounds.Width;
        }

        private Bitmap CaptureArea(bool update_pic_box)
        {
            if(update_pic_box)
                pictureBox1.Visible = true;

            Rectangle bounds = new Rectangle(0, 0, CaptureRight - CaptureLeft, CaptureBottom - CaptureTop);
            Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
            Graphics g = Graphics.FromImage(bitmap);
            g.CopyFromScreen(CaptureLeft, CaptureTop, 0, 0, bounds.Size);

            if (update_pic_box)
                pictureBox1.Image = bitmap;

            return bitmap;
        }
        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (CaptureMode == CaptureModeEnum.Top)
                CaptureTop +=(int)( (double) e.Y * (double) (CaptureBottom - CaptureTop) / pictureBox1.Height );
            else if (CaptureMode == CaptureModeEnum.Bottom)
                CaptureBottom -= (int)((double)(pictureBox1.Height - e.Y) * (double)(CaptureBottom - CaptureTop) / pictureBox1.Height);
            else if(CaptureMode == CaptureModeEnum.Left)
                CaptureLeft += (int)((double)e.X * (double)(CaptureRight - CaptureLeft) / pictureBox1.Width);
            else if(CaptureMode == CaptureModeEnum.Rght)
                CaptureRight -= (int)((double)(pictureBox1.Width - e.X) * (double)(CaptureRight - CaptureLeft) / pictureBox1.Width);

            if (CaptureMode != CaptureModeEnum.None)
                CaptureArea(true);
        }
        private void resetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetDefaultCaptureArea();

            CaptureArea(true);
        }
        private void topToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CaptureMode = CaptureModeEnum.Top;
        }
        private void bottomToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CaptureMode = CaptureModeEnum.Bottom;
        }
        private void leftToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CaptureMode = CaptureModeEnum.Left;
        }
        private void rightToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CaptureMode = CaptureModeEnum.Rght;
        }
        private void showUserDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            pictureBox1.Visible = false;
            txtDiag.Visible = false;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Enabled = false;

            Bitmap b = CaptureArea(false);

            //string fname = CaptureDataDirectory + dt.ToString("HHmmss.ff") + ".bmp";
            //b.Save(fname, ImageFormat.Bmp);

            bool isHeaterOn = false;
            double Temp = 0;
            TimeSpan ts = DateTime.Now - StartCaptureTime;

            if (BitmapOCR(b, ref isHeaterOn, ref Temp, null))
            {
                PidData.Add(new PidDataStruct(ts.TotalSeconds, Temp, isHeaterOn));
                txtTime.Text = ts.TotalSeconds.ToString("0.00");
                txtTemp.Text = Temp.ToString();
                txtHeater.Text = isHeaterOn ? "ON" : "OFF";
            }
            else
            {
                txtTime.Text = ts.TotalSeconds.ToString("0.00");
                txtTemp.Text = "ERROR";
                txtHeater.Text = "";
            }

            timer1.Enabled = true;
        }

        List<string> BmpNames = new List<string>();
        int CurrentBmpIndex = 0;

        private void recognizeImageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            txtDiag.Visible = true;
            txtDiag.Clear();
            string[] fnames = Directory.GetFiles(CaptureDataDirectory, "*.bmp", SearchOption.AllDirectories);

            BmpNames.Clear();

            foreach (string f in fnames)
            {
                BmpNames.Add(f);

                // diagnostic for a single file
                //if (f != CaptureDataDirectory + "142408.37.bmp")
                //    continue;

                Bitmap bmp = new Bitmap(f);

                //pictureBox1.Visible = true;
                //pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
                //pictureBox1.Image = bmp;


                bool isHeaterOn = false;
                double Temp = 0;
                string Time = Path.GetFileNameWithoutExtension(f);

                if(BitmapOCR(bmp, ref isHeaterOn, ref Temp, null))
                    txtDiag.AppendText(Time + "   " + (isHeaterOn ? " H " : " - ") + " " + Temp.ToString() + "\r\n");
                else
                    txtDiag.AppendText(Time + " failed  " + "\r\n");
            }
        }

        private bool BitmapOCR (Bitmap bmp, ref bool isHeaterOn, ref double Temp, StringBuilder log)
        {
            isHeaterOn = false;
            Temp = 0.0;

            SimpleOcr OCR = new SimpleOcr();

            OCR.ToBlackWhite(bmp);

            List<Bitmap> bit_words = new List<Bitmap>();
            if (!OCR.ChopBitmapIntoWords(bmp, ref bit_words))
                return false;

            if (bit_words.Count == 0)
                return false;

            //bmp.Save(fname.Replace("bmp", "new") + ".bmp", ImageFormat.Bmp);
            //for (int i = 0; i < bit_words.Count; i++)
            //  bit_words[i].Save(CaptureDataDirectory + i.ToString() + ".bmp");

            OCR.FillCandidateList(bit_words[0].Height);

            double digit_multiplier = 1;
            if(DigitLocation == 1) digit_multiplier = 0.1;
            else if (DigitLocation == 2) digit_multiplier = 0.01;
            else if (DigitLocation == 3) digit_multiplier = 0.001;

            for (int b_id = bit_words.Count - 1; b_id >= 0; b_id--)
            {
                if (log != null) { log.AppendLine(); log.AppendLine("Blob => " + b_id.ToString()); }

                // ====== Simple OCR ==========

                int decimal_dot_location = 1 + (int)(0.5 + SimpleOcr.Segment.scale * OCR.ProtoT);
                List<SimpleOcr.BlobInfo> blobs = new List<SimpleOcr.BlobInfo>();
                OCR.DetectRegions(bit_words[b_id], decimal_dot_location, ref blobs);

                int simple_ocr_output = OCR.MatchSimpleCandidate(blobs, bit_words[b_id].Width, bit_words[b_id].Height);

                if (log != null)
                {
                    foreach (SimpleOcr.BlobInfo b in blobs)
                        log.AppendLine(b.GetBlobType().ToString() + " " + b.xmin.ToString() + " " + b.xmax.ToString() + " / " +
                                           b.ymin.ToString() + " " + b.ymax.ToString());
                    log.AppendLine("Detected => " + simple_ocr_output.ToString());
                    log.AppendLine(""); log.AppendLine("----------- ");
                }

                // simple OCR cannot detect the digit
                if (simple_ocr_output == -1)
                {
                    // check if this is the first blob, i.e. the heater status
                    if (b_id == 0)
                    {
                        int ymin = int.MaxValue;
                        foreach (SimpleOcr.BlobInfo b in blobs)
                            ymin = Math.Min(ymin, b.ymin);

                        // the heater status blobs are all above the center of the image
                        if (ymin > bit_words[b_id].Height / 2)
                        {
                            isHeaterOn = true;
                            break;
                        }
                    }
                }
                else // simple OCR is fine, save the digit and continue with the next
                {
                    Temp += digit_multiplier * simple_ocr_output;
                    digit_multiplier *= 10;
                    continue;
                }

                // ====== Simple OCR2 ==========
                List<int> ray_width = new List<int>();
                if (!OCR.DetectRays(bit_words[b_id], decimal_dot_location, ref ray_width))
                    return false;

                int simple_ocr_output2 = OCR.MatchSimpleCandidate2(ray_width);
                if (simple_ocr_output2 == -1)
                    return false;

                Temp += digit_multiplier * simple_ocr_output2;
                digit_multiplier *= 10;


                //string sss = "";
                //foreach (int w in ray_width)
                //    sss += w.ToString() + " ";
                //txtDiag.AppendText("OCR2 for bit " + b_id.ToString() + " " + status.ToString() + " " + sss + "\r\n");

                // ====== Proper OCR - does not work correctly... ==========
                /*
                bool cut_into_separate_outlines = false;
                List<List<SimpleOcr.Pair>> outlines = new List<List<SimpleOcr.Pair>>();
                List<SimpleOcr.Pair> common_outline = OCR.MakeOutline(bit_words[b_id], decimal_dot_location, cut_into_separate_outlines, ref outlines);
                List<double> evidence = OCR.CalcCandidateEvidence(common_outline);

                int proper_ocr_output = 0;
                double best_evidence = Double.MinValue;
                for (int i = 0; i < evidence.Count; i++)
                {
                    if (evidence[i] > best_evidence)
                    {
                        best_evidence = evidence[i];
                        proper_ocr_output = i;
                    }
                    if (log != null)
                        log.AppendLine("cand=" + i.ToString() + " " + evidence[i].ToString());
                }
                if (log != null)
                    log.AppendLine("best=" + proper_ocr_output.ToString());

                Temp += digit_multiplier * proper_ocr_output;
                digit_multiplier *= 10;
                */
            }

            return true;
        }
    }
}
